package net.tarine.gastronomi.server.services;

import java.util.ArrayList;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;

import net.tarine.gastronomi.client.services.GasService;
import net.tarine.gastronomi.server.ToConverter;
import net.tarine.gastronomi.server.model.Gas;
import net.tarine.gastronomi.server.model.GasUser;
import net.tarine.gastronomi.server.persistence.GasUserDao;
import net.tarine.gastronomi.server.persistence.PmFactory;
import net.tarine.gastronomi.shared.AppConst;
import net.tarine.gastronomi.shared.DatabaseException;
import net.tarine.gastronomi.shared.EmptyResultException;
import net.tarine.gastronomi.shared.ValidationException;
import net.tarine.gastronomi.shared.to.GasTo;
import net.tarine.gastronomi.shared.to.GasUserTo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
public class GasServiceImpl extends RemoteServiceServlet implements GasService {
	private static final long serialVersionUID = -2212240530095265405L;

	private static final Log LOG = LogFactory.getLog(GasServiceImpl.class);

	@Override
	public List<GasUserTo> findGasUsers(Integer offset, Integer pageSize)
			throws DatabaseException, EmptyResultException {
		List<GasUserTo> result = new ArrayList<GasUserTo>();
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		try {
			List<GasUser> items = GasUserDao.findGasUsers(pm, offset, pageSize);
			for (GasUser gu:items) {
				result.add(ToConverter.convert(pm, gu));
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		if (result != null) {
			if (result.size() > 0) {
				return result;
			}
		}
		throw new EmptyResultException("Non e' stato trovato alcun utente");
	}



	@Override
	public GasUserTo findGasUserById(String id) throws DatabaseException, EmptyResultException {
		GasUserTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		pm.setDetachAllOnCommit(true);
		try {
		    GasUser item = pm.getObjectById(GasUser.class, id);
		    result = ToConverter.convert(pm, item);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		if (result != null) {
			return result;
		}
		throw new EmptyResultException("Non e' stato trovato alcun utente con id="+id);
	}



	/**
	 * Acquisisce l'utente google se già nella sessione.
	 * Se lo trova cerca lo stesso userId tra i GasUserTo abilitati.
	 * Se ok restituisce il GasUserTo.
	 * Altrimenti:
	 *   Se è admin crea il GasUserTo dall'utente Google e lo restituisce.
	 *   Altrimenti:
	 *     Non è admin quindi cerca tra i GasUserTo cercando solo l'email.
	 *     Se trova l'email allora crea un GasUserTo completo arricchendo l'entità GasUserTo con i dati di Google.
	 *     Altrimenti: restituisce null.
	 * 
	 * Il metodo lancia ValidationException se l'utente google non è null ma
	 * è null la ricerca degli utenti abilitati sopra descritta.
	 */
	@Override
	public GasUserTo findGasUserFromSession() throws DatabaseException, EmptyResultException, ValidationException {
		//Google user
        UserService userService = UserServiceFactory.getUserService();
        User user = userService.getCurrentUser();
        boolean admin = false;
        if (userService.isUserLoggedIn()) {
        	admin = userService.isUserAdmin();
        }
        //query or creation
        GasUserTo result = null;
		if (user != null) {
			String userId = user.getUserId();
			if (userId == null) throw new DatabaseException("L'utente non ha identificativo");
			PersistenceManager pm = PmFactory.get().getPersistenceManager();
			Transaction tx = pm.currentTransaction();
			try {
				tx.begin();
				GasUser item = GasUserDao.findGasUserByUserId(pm, userId);
				if (item == null) {
					if (admin) {
						item = GasUserDao.createGasUserFromGoogleUser(pm, user, admin);
					} else {
						item = GasUserDao.findGasUserByEmail(pm, user.getEmail());
						if (item != null) {
							item = GasUserDao.enrichGasUserFromGoogleUser(pm, item, user, admin);
						}
					}
				}
				tx.commit();
				result = ToConverter.convert(pm, item);
			} catch (Exception e) {
				LOG.error(e.getMessage(), e);
				tx.rollback();
				throw new DatabaseException(e);
			} finally {
			    pm.close();
			}
			if (result == null) {
				throw new ValidationException("L'utente non &egrave; abilitato a questa applicazione");
			}
		}
		if (result == null) {
			throw new EmptyResultException("L'utente non ha effettuato login");
		}
		return result;
	}



	@Override
	public GasUserTo persistGasUser(GasUserTo to) throws DatabaseException {
		GasUserTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    GasUser item = ToConverter.convert(pm, to);
		    item = pm.makePersistent(item);
		    tx.commit(); // Commit the PM transaction
		    result = ToConverter.convert(pm, item);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return result;
	}



	@Override
	public List<GasUserTo> deleteGasUser(String id) throws DatabaseException, EmptyResultException {
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    GasUser persistent = pm.getObjectById(GasUser.class, id);
		    pm.deletePersistent(persistent);
		    tx.commit(); // Commit the PM transaction
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return findGasUsers(0, AppConst.PAGING_ROWS);
	}


	@Override
	@SuppressWarnings("unchecked")
	public GasTo findGas() throws DatabaseException {
		GasTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin(); // Start the PM transaction
			Gas gas = null;
		    List<Gas> gList = (List<Gas>) pm.newQuery(Gas.class).execute();
		    if (gList != null) {
		    	if (gList.size() > 0) {
		    		gas = gList.get(0);
		    	}
		    }
		    if (gas == null) {
		    	gas = pm.makePersistent(new Gas());
		    }
		    result = ToConverter.convert(pm, gas);
		    tx.commit(); // Commit the PM transaction
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return result;
	}



	@Override
	public GasTo persistGas(GasTo to) throws DatabaseException {
		GasTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    Gas item = ToConverter.convert(pm, to);
		    item = pm.makePersistent(item);
		    tx.commit(); // Commit the PM transaction
		    result = ToConverter.convert(pm, item);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return result;
	}


	@Override
	public String createLoginUrl(String destinationUrl) throws EmptyResultException {
		com.google.appengine.api.users.UserService userService = UserServiceFactory.getUserService();
		String result = userService.createLoginURL(destinationUrl);
		if (result != null) {
			if (result.length() > 0) {
				return result;
			}
		}
		throw new EmptyResultException("Impossibile ottenere URL di login");
	}



	@Override
	public String createLogoutUrl(String destinationUrl) throws EmptyResultException {
		com.google.appengine.api.users.UserService userService = UserServiceFactory.getUserService();
		String result = userService.createLogoutURL(destinationUrl);
		if (result != null) {
			if (result.length() > 0) {
				return result;
			}
		}
		throw new EmptyResultException("Impossibile ottenere URL di logout");
	}




}